package com.wzcl.app.service.impl;

import com.alibaba.fastjson.JSON;
import com.wzcl.app.dao.model.*;
import com.wzcl.app.dao.repository.TopicRepository;
import com.wzcl.app.model.common.BusinessException;
import com.wzcl.app.model.common.CodeMsg;
import com.wzcl.app.model.common.MyPage;
import com.wzcl.app.model.dto.KnowledgePointDto;
import com.wzcl.app.model.dto.TopicDto;
import com.wzcl.app.service.KnowledgePointService;
import com.wzcl.app.service.TopicAndKnowledgePointService;
import com.wzcl.app.service.TopicService;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.criteria.*;
import java.util.*;

@Log4j2
@Service
@Transactional
public class TopicServiceImpl implements TopicService {

    @Autowired
    private TopicRepository repository;

    @Autowired
    private KnowledgePointService knowledgePointService;

    @Autowired
    private TopicAndKnowledgePointService topicAndKnowledgePointService;


    //添加一个试题
    @Override
    public Topic addTopic(TopicDto topicDto) {
        //1.创建基本的题目数据
        Topic topic = new Topic();
        topic.setType(topicDto.getType());
        topic.setHard(topicDto.getHard());
        topic.setMaster(topicDto.getMaster());
        topic.setDescription(topicDto.getDescription());
        topic.setScore(0);
        return repository.save(topic);
    }

    @Override
    public Topic delTopicById(Integer id) {
        Topic topic = getAndCheckById(id);
        //删除题目前 维护中间表关系
        topicAndKnowledgePointService.unBind(topic);
        //删除数据
        log.info("Topic:{} Physical deletion", JSON.toJSONString(topic));
        repository.delete(topic);
        return topic;
    }

    @Override
    public void updateBasicTopic(TopicDto topicDto) {
        Topic topic = getAndCheckById(topicDto);
        //更新难度和题目和描述
        topic.setHard(topicDto.getHard());
        topic.setMaster(topicDto.getMaster());
        topic.setDescription(topicDto.getDescription());
        repository.save(topic);
    }

    @Override
    public void updateTopicKnowledgePoint(TopicDto topicDto) {
        if (topicDto == null || topicDto.getIsAddKnowledgePoint() == null || topicDto.getKnowledgePoint() == null || !topicDto.getKnowledgePoint().isValid()) {
            throw new BusinessException(CodeMsg.MUST_BE_NOT_NULL);
        }
        Topic topic = getAndCheckById(topicDto);
        KnowledgePoint knowledgePoint = knowledgePointService.findByProperty(topicDto.getKnowledgePoint());
        if (topicDto.getIsAddKnowledgePoint()) {
            //添加一个知识点
            topicAndKnowledgePointService.bind(topic, knowledgePoint);
        } else {
            //删除一个知识点
            topicAndKnowledgePointService.unBind(topic, knowledgePoint);
        }
    }


    @Override
    public Topic getTopicById(Integer id) {
        return repository.findById(id).orElse(null);
    }

    @Override
    public Topic getAndCheckById(Integer id) {
        if (id == null) {
            throw new BusinessException(CodeMsg.COMMODITY_NOT_EXIST, "缺少题目Id");
        }
        Topic topic = getTopicById(id);
        if (topic == null) {
            throw new BusinessException(CodeMsg.TOPIC_NOT_EXIST);
        }
        return topic;
    }

    //刷新题目总分
    @Override
    public Topic refreshScore(Topic topic) {
        int score = 0;
        List<TopicInfo> topicInfoList = topic.getTopicInfoList();
        if (topicInfoList != null && topicInfoList.size() > 0) {
            for (TopicInfo topicInfo : topicInfoList) {
                score += topicInfo.getScore();
            }
        }
        topic.setScore(score);
        return repository.save(topic);
    }

    @Override
    public List<Topic> getTopicList(TopicDto topicDto) {
        return repository.findAll(getSpecification(topicDto), topicDto.getSort());
    }

    @Override
    public MyPage<Topic> getTopicPage(TopicDto topicDto) {
        Page<Topic> all = repository.findAll(getSpecification(topicDto), topicDto.getPageable());
        ArrayList<Topic> list = new ArrayList<>(all.getContent());
        Collections.reverse(list);
        MyPage<Topic> myPage = new MyPage<>();
        myPage.setContent(list);
        myPage.setTotalElements(all.getTotalElements());
        return myPage;
    }

    @Override
    public List<Topic> getTopicRandList(TopicDto topicDto) {
        //题目数校验
        if (topicDto.getCount() < 1) {
            return Collections.emptyList();
        }
        Specification<Topic> specification = getSpecification(topicDto);
        long count = repository.count(specification);
        if (count == 0) {
            //没有满足的题目
            return Collections.emptyList();
        }
        int total = (int) count;
        if (topicDto.getCount() >= total) {
            //满足条件的题目数不足
            return repository.findAll(specification);
        } else {
            Set<Integer> randomSet = new LinkedHashSet<>();
            Random random = new Random();
            do {
                randomSet.add(random.nextInt(total));
            } while (randomSet.size() < topicDto.getCount());
            ArrayList<Topic> list = new ArrayList<>();
            randomSet.forEach(integer -> {
                PageRequest pageRequest = PageRequest.of(integer, 1);
                list.addAll(repository.findAll(specification, pageRequest).getContent());
            });
            return list;
        }
    }

    private Specification<Topic> getSpecification(TopicDto topicDto) {
        return new Specification<Topic>() {
            @Override
            public Predicate toPredicate(Root<Topic> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                List<Predicate> predicates = new ArrayList<Predicate>();
                if (topicDto.getType() != null) {
                    predicates.add(criteriaBuilder.equal(root.get("type"), topicDto.getType()));
                }
                if (topicDto.getKnowledgePoint() != null) {
                    KnowledgePointDto knowledgePointDto = topicDto.getKnowledgePoint();
                    Join<Topic, TopicAndKnowledgePoint> topicAndKnowledgePointPath = root.join(root.getModel().getList("topicAndKnowledgePointList", TopicAndKnowledgePoint.class), JoinType.LEFT);
                    Join<TopicAndKnowledgePoint, KnowledgePoint> knowledgePointPath = topicAndKnowledgePointPath.join("knowledgePoint", JoinType.LEFT);
                    if (knowledgePointDto.getGradeId() != null) {
                        predicates.add(criteriaBuilder.equal(knowledgePointPath.get("gradeId"), knowledgePointDto.getGradeId()));
                    }
                    if (knowledgePointDto.getSubjectId() != null) {
                        predicates.add(criteriaBuilder.equal(knowledgePointPath.get("subjectId"), knowledgePointDto.getSubjectId()));
                    }
                    if (StringUtils.isNotBlank(knowledgePointDto.getChapter())) {
                        predicates.add(criteriaBuilder.equal(knowledgePointPath.get("chapter"), knowledgePointDto.getChapter()));
                    }
                    if (StringUtils.isNotBlank(knowledgePointDto.getPoint())) {
                        predicates.add(criteriaBuilder.like(knowledgePointPath.get("point"), "%" + knowledgePointDto.getPoint() + "%"));
                    }
                }
                return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
            }
        };
    }
}
